﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using PostSharp.Aspects;
using PostSharp.Extensibility;
using System.Reflection;
using SecurityAgent;

namespace TSASResourceServiceImpl
{
    [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
    [Serializable]
    public class ObjectProtectionAttribute : OnMethodBoundaryAspect
    {
        private string objectName;

        public string ObjectPrefix { get; set; }
        public int ObjectIDParameter { get; set; }
        public string ObjectOperation { get; set; }
        
        public override bool CompileTimeValidate( MethodBase method )
        {
            ParameterInfo[] parameters = method.GetParameters();
            
            if (this.ObjectIDParameter < 0)
            {
                if ( method.IsStatic )
                {
                    Message.Write(PostSharp.MessageLocation.Unknown, SeverityType.Error, "MY001", "Error with [ObjectProtection] applied to {0}.{1}: the method cannot be static when the Parameter property is not set.",
                              method.DeclaringType.FullName, method.Name);
                    return false;
                }

                //objectName = method.Name;

            }
            else
            {
                if (this.ObjectIDParameter >= parameters.Length)
                {
                    Message.Write(PostSharp.MessageLocation.Unknown, SeverityType.Error, "MY002", "Error with [ObjectProtection] applied to {0}.{1}: not enough parameters",
                              method.DeclaringType.FullName, method.Name);
                    return false;

                }

                this.objectName = string.Empty;     // when valid parameter number is specified, the object name is obtained from the parameter

            }

            if (parameters.Length == 0)
            {
                Message.Write(PostSharp.MessageLocation.Unknown, SeverityType.Error, "MY003", "Error with [ObjectProtection] applied to {0}.{1}: no userToken provided",
                              method.DeclaringType.FullName, method.Name);
                return false;

            }
            else
            {
                if (parameters[0].ParameterType != typeof(string))
                {
                    Message.Write(PostSharp.MessageLocation.Unknown, SeverityType.Error, "MY003", "Error with [ObjectProtection] applied to {0}.{1}: a string userToken must be the first parameter",
                              method.DeclaringType.FullName, method.Name);
                    return false;
                }
            }

            return true;
        }

        public override void OnEntry( MethodExecutionArgs args )
        {
            string objectName = string.Empty;

            if (this.ObjectIDParameter < 0)
            {
                objectName = args.Method.Name;
            }
            else
            {
                objectName = ( String.IsNullOrEmpty(ObjectPrefix) ? "" : ObjectPrefix) + args.Arguments[ObjectIDParameter].ToString().Trim();
            }

            if (String.IsNullOrEmpty(objectName)) return;

            string userToken = args.Arguments[0].ToString().Trim();

            SecurityAgent.AccessLevel access;
            
            try
            {
                 access = SecurityAgent.ObjectSecurity.Client.ObjectSecurityAgent.CheckObjectAccess(objectName, userToken);
            }
            catch (SecurityAgent.UnauthorizedAccessException e1)
            {
                throw new FaultException<UnauthorizedAccessFault>(e1.Fault);
            }
            catch (Exception e3)
            {
                throw new FaultException(e3.Message);
            }

            if ((access.AllowRead && (this.ObjectOperation == "READ" || String.IsNullOrEmpty(this.ObjectOperation)))
               || (access.AllowUpdate && (this.ObjectOperation == "UPDATE"))
               || (access.AllowDelete && (this.ObjectOperation == "DELETE"))
               || (access.AllowCreate && (this.ObjectOperation == "CREATE")))
            {
                return;
            }
            else
            {
                throw new FaultException<UnauthorizedAccessFault>(new UnauthorizedAccessFault { Application = args.Method.DeclaringType.Name, Object = objectName, Operation = ObjectOperation, UserToken= userToken });
            }

        }
        
        
    }

}
